#include "slidemodel.h"
#include <iostream>
#define NULL_TUPLE_ITR m_tupleCollection.end()

SlideModel::SlideModel(void)
{
    m_cachedTemplateDoc = new QDomDocument("LinePlan");
    m_outputDoc = new QDomDocument("LinePlan");
    m_collectionHasRoot = false;
}

bool SlideModel::InitialiseOutputDocument(const char* styleSheet){
    //In a nutshell, clears the output document, copies in the xml encoding header and copies in the
    //static elements from the source template document's root node

    if (m_cachedTemplateDoc->firstChild().isNull()){
        std::cout << "Couldn't initialise output model, input template is not yet cached!\n";
        return false;
    }

    if (m_cachedTemplateDoc->documentElement().tagName() != "svg"){
        std::cout << "Couldn't initialise output model, template is not an SVG file!\n";
        return false;
    }    


    //Removing Document Encoding and Root Nodes by name yields predicatable initialisation
    if (!m_outputDoc->firstChild().toProcessingInstruction().isNull()) m_outputDoc->removeChild(m_outputDoc->firstChild());
    m_outputDoc->removeChild(m_outputDoc->documentElement());


    //Not using cloneNode() would extract the node from the source document
    QDomProcessingInstruction encodingHeader = m_cachedTemplateDoc->firstChild().cloneNode(false).toProcessingInstruction();
    if (!encodingHeader.isNull()){
        encodingHeader.setData("xml-stylesheet version=\"1.0\" encoding=\"UTF-8\" type=\"text/css\" href=\""
                               + QString::fromAscii(styleSheet) + "\"");
        m_outputDoc->appendChild(encodingHeader);
    }
    else
        std::cout << "Document encoding and Stylesheet declaration was not found!\n";


    m_outputXmlRoot = AddTuple(Tuple(NULL_TUPLE_ITR, m_cachedTemplateDoc->documentElement()));

    //Parse dynamic elements from the cached model and add as child tuples to outputXmlRoot
    QDomElement em;
    QDomElement em1;
    em = m_outputXmlRoot->self.firstChildElement();

    int c = 0;
    while (!em.isNull()){

        if(tagNameIsDynamic(em.tagName())){
            AddTuple(Tuple(m_outputXmlRoot, em));
            c++;
            em1 = em;
        }

        em = em.nextSiblingElement();
        m_outputXmlRoot->self.removeChild(em1);

    }

    std::cout << ((c == 0)?"Warning! ":"") << c << " dynamic element(s) found\n";

    return true;
}

TupleItr SlideModel::AddTuple(Tuple pair){

    //Ensures that only one 'root' tuple exists
    if (pair.parent == NULL_TUPLE_ITR && m_collectionHasRoot){
        std::cout << "Cannot add element pair with a null Parent!\n";
        return NULL_TUPLE_ITR;
    }

    //provides an iterator to the element just inserted to be used as a reference
    //for any children to be added
    m_tupleCollection.push_back(pair);
    m_collectionHasRoot = true;
    return --m_tupleCollection.end();

}

void SlideModel::RemoveTuple(TupleItr& pair){
    //TODO: Recursive deletion of Tuples to maintain tree integrity
    m_tupleCollection.erase(pair);
}

TupleItr SlideModel::GetRootTuple(void){

    //The root tuple is the only one that points to list::back();
    for (TupleItr root = m_tupleCollection.begin(); root != m_tupleCollection.end(); root++){
        if (root->parent == NULL_TUPLE_ITR)
            return root;
    }

    std::cout << "No root element in this model! Is model initialised?\n";

    return NULL_TUPLE_ITR;
}

TupleItr SlideModel::GetNullTuple(void){
    return NULL_TUPLE_ITR;
}

TupleItr SlideModel::GetFirstTupleWithParent(TupleItr parent)
{

    TupleItr p = m_tupleCollection.begin();
    for (; p != NULL_TUPLE_ITR; p++){
        if (p->parent == parent) break;
    }

    return p;
}

TupleItr SlideModel::GetNextSiblingForTuple(TupleItr element){

    TupleItr p = element;
    p++;

    for (; p != NULL_TUPLE_ITR; p++){

        if (p->parent == element->parent && p != element) break;

    }

    return p;

}

void SlideModel::CacheContentsFromFile(QFile& inputTemplate){

    m_cachedTemplateDoc->clear();
    std::cout << ((m_cachedTemplateDoc->setContent(&inputTemplate)) ?
                     "Done!":"Template invalid!") << "\n";

}

QByteArray SlideModel::GetOutputDocAsByteArray(void){

    return m_outputDoc->toByteArray(5);

}

void SlideModel::FinaliseOutputDocument(void){

    m_outputDoc->appendChild(getTupleRecursively(m_outputXmlRoot));
    m_tupleCollection.clear();
    m_collectionHasRoot = false;

}

/////////////////////
///Private Methods///
/////////////////////

QDomNode SlideModel::getTupleRecursively(TupleItr& emItr){
    //Traverses the DOM tree represented in the Tuple List and recursively
    //appends children nodes to their parents until a full svg document
    //is developed

    TupleItr c1, c2;
    c1 = GetFirstTupleWithParent(emItr);

    while (c1 != NULL_TUPLE_ITR){

        emItr->self.appendChild(getTupleRecursively(c1));
        c2 = c1;
        c1 = GetNextSiblingForTuple(c2);
        RemoveTuple(c2);

    }

    return emItr->self;

}

bool SlideModel::tagNameIsDynamic(QString tagName){

    //NOTE:
    //What would be a better way of handling a dynamic tag vocabulary? Compare against static
    //enumeration? Definitions file? Introspection of PaintBox library and call painting methods
    //by name?

    QString dynamicTags[5] = {"lineplan", "dyntext", "multitext", "graphic", "terminal"};

    for (int i = 0; i < 5; i++){
        if (tagName == dynamicTags[i]) return true;
    }

    return false;
}

SlideModel::~SlideModel(void)
{
    delete m_cachedTemplateDoc;
    delete m_outputDoc;
}
